home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 5 / Apprentice-Release5.iso / Source Code / C / Applications / Python 1.3.3 / Python 133 SRC / Lib / formatter.py < prev    next >
Text File  |  1996-04-25  |  11KB  |  387 lines

  1. import regex
  2. import regsub
  3. import string
  4. import sys
  5. from string import split, join
  6. from types import StringType
  7.  
  8.  
  9. AS_IS = None
  10.  
  11.  
  12. class NullFormatter:
  13.  
  14.     def __init__(self): pass
  15.     def end_paragraph(self, blankline): pass
  16.     def add_line_break(self): pass
  17.     def add_hor_rule(self, abswidth=None, percentwidth=1.0,
  18.              height=None, align=None): pass
  19.     def add_label_data(self, format, counter): pass
  20.     def add_flowing_data(self, data): pass
  21.     def add_literal_data(self, data): pass
  22.     def flush_softspace(self): pass
  23.     def push_alignment(self, align): pass
  24.     def pop_alignment(self): pass
  25.     def push_font(self, x): pass
  26.     def pop_font(self): pass
  27.     def push_margin(self, margin): pass
  28.     def pop_margin(self): pass
  29.     def set_spacing(self, spacing): pass
  30.     def push_style(self, *styles): pass
  31.     def pop_style(self, n=1): pass
  32.     def assert_line_data(self, flag=1): pass
  33.  
  34.  
  35. class AbstractFormatter:
  36.  
  37.     def __init__(self, writer):
  38.     self.writer = writer        # Output device
  39.     self.align = None        # Current alignment
  40.     self.align_stack = []        # Alignment stack
  41.     self.font_stack = []        # Font state
  42.     self.margin_stack = []        # Margin state
  43.     self.spacing = None        # Vertical spacing state
  44.     self.style_stack = []        # Other state, e.g. color
  45.     self.nospace = 1        # Should leading space be suppressed
  46.     self.softspace = 0        # Should a space be inserted
  47.     self.para_end = 1        # Just ended a paragraph
  48.     self.hard_break = 1        # Have a hard break
  49.     self.have_label = 0
  50.  
  51.     def end_paragraph(self, blankline):
  52.     if not self.hard_break:
  53.         self.writer.send_line_break()
  54.         self.have_label = 0
  55.     if not self.para_end:
  56.         self.writer.send_paragraph((blankline and 1) or 0)
  57.         self.have_label = 0
  58.     self.hard_break = self.nospace = self.para_end = 1
  59.     self.softspace = 0
  60.  
  61.     def add_line_break(self):
  62.     if not (self.hard_break or self.para_end):
  63.         self.writer.send_line_break()
  64.         self.have_label = 0
  65.     self.hard_break = self.nospace = 1
  66.     self.softspace = 0
  67.  
  68.     def add_hor_rule(self, abswidth=None, percentwidth=1.0,
  69.              height=None, align=None):
  70.     if not self.hard_break:
  71.         self.writer.send_line_break()
  72.     self.writer.send_hor_rule(abswidth, percentwidth, height, align)
  73.     self.hard_break = self.nospace = 1
  74.     self.have_label = self.para_end = self.softspace = 0
  75.  
  76.     def add_label_data(self, format, counter):
  77.     if self.have_label:
  78.         self.add_line_break()
  79.     if type(format) is StringType:
  80.         self.writer.send_label_data(self.format_counter(format, counter))
  81.     else:
  82.         self.writer.send_label_data(format)
  83.     self.nospace = self.have_label = self.hard_break = self.para_end = 1
  84.     self.softspace = 0
  85.  
  86.     def format_counter(self, format, counter):
  87.         label = ''
  88.         for c in format:
  89.             try:
  90.                 if c == '1':
  91.             label = label + ('%d' % counter)
  92.                 elif c in 'aA':
  93.             if counter > 0:
  94.             label = label + self.format_letter(c, counter)
  95.                 elif c in 'iI':
  96.             if counter > 0:
  97.             label = label + self.format_roman(c, counter)
  98.         else:
  99.             label = label + c
  100.             except:
  101.                 label = label + c
  102.         return label
  103.  
  104.     def format_letter(self, case, counter):
  105.     label = ''
  106.     while counter > 0:
  107.         counter, x = divmod(counter-1, 26)
  108.         s = chr(ord(case) + x)
  109.         label = s + label
  110.     return label
  111.  
  112.     def format_roman(self, case, counter):
  113.         ones = ['i', 'x', 'c', 'm']
  114.         fives = ['v', 'l', 'd']
  115.         label, index = '', 0
  116.     # This will die of IndexError when counter is too big
  117.         while counter > 0:
  118.             counter, x = divmod(counter, 10)
  119.             if x == 9:
  120.                 label = ones[index] + ones[index+1] + label
  121.             elif x == 4:
  122.                 label = ones[index] + fives[index] + label
  123.             else:
  124.                 if x >= 5:
  125.                     s = fives[index]
  126.                     x = x-5
  127.                 else:
  128.                     s = ''
  129.                 s = s + ones[index]*x
  130.         label = s + label
  131.             index = index + 1
  132.         if case == 'I':
  133.         return string.upper(label)
  134.         return label
  135.  
  136.     def add_flowing_data(self, data, whitespace=string.whitespace,
  137.              join = join, split = split):
  138.     if not data: return
  139.     # The following looks a bit convoluted but is a great improvement over
  140.     # data = regsub.gsub('[' + string.whitespace + ']+', ' ', data)
  141.     prespace = data[:1] in whitespace
  142.     postspace = data[-1:] in whitespace
  143.     data = join(split(data))
  144.     if self.nospace and prespace:
  145.         if not data: return
  146.         prespace = 0
  147.     elif self.softspace:
  148.         prespace = 1
  149.         data = ' ' + data
  150.     elif prespace:
  151.         data = ' ' + data
  152.     self.hard_break = self.nospace = self.para_end = self.have_label = 0
  153.     self.softspace = postspace
  154.     self.writer.send_flowing_data(data)
  155.  
  156.     def add_literal_data(self, data):
  157.     if not data: return
  158.     #  Caller is expected to cause flush_softspace() if needed.
  159.     self.hard_break = data[-1:] == '\n'
  160.     self.nospace = self.para_end = self.softspace = self.have_label = 0
  161.     self.writer.send_literal_data(data)
  162.  
  163.     def flush_softspace(self):
  164.     if self.softspace:
  165.         self.hard_break = self.nospace = self.para_end = \
  166.                   self.have_label = self.softspace = 0
  167.         self.writer.send_flowing_data(' ')
  168.  
  169.     def push_alignment(self, align):
  170.     if align and align != self.align:
  171.         self.writer.new_alignment(align)
  172.         self.align = align
  173.         self.align_stack.append(align)
  174.     else:
  175.         self.align_stack.append(self.align)
  176.  
  177.     def pop_alignment(self):
  178.     if self.align_stack:
  179.         del self.align_stack[-1]
  180.     if self.align_stack:
  181.         self.align = align = self.align_stack[-1]
  182.         self.writer.new_alignment(align)
  183.     else:
  184.         self.align = None
  185.         self.writer.new_alignment(None)
  186.  
  187.     def push_font(self, (size, i, b, tt)):
  188.     if self.softspace:
  189.         self.hard_break = self.nospace = self.para_end = self.softspace = 0
  190.         self.writer.send_flowing_data(' ')
  191.     if self.font_stack:
  192.         csize, ci, cb, ctt = self.font_stack[-1]
  193.         if size is AS_IS: size = csize
  194.         if i is AS_IS: i = ci
  195.         if b is AS_IS: b = cb
  196.         if tt is AS_IS: tt = ctt
  197.     font = (size, i, b, tt)
  198.     self.font_stack.append(font)
  199.     self.writer.new_font(font)
  200.  
  201.     def pop_font(self):
  202.     if self.softspace:
  203.         self.hard_break = self.nospace = self.para_end = self.softspace = 0
  204.         self.writer.send_flowing_data(' ')
  205.     if self.font_stack:
  206.         del self.font_stack[-1]
  207.     if self.font_stack:
  208.         font = self.font_stack[-1]
  209.     else:
  210.         font = None
  211.     self.writer.new_font(font)
  212.  
  213.     def push_margin(self, margin):
  214.     self.margin_stack.append(margin)
  215.     self.writer.new_margin(margin, len(self.margin_stack))
  216.  
  217.     def pop_margin(self):
  218.     if self.margin_stack:
  219.         del self.margin_stack[-1]
  220.     if self.margin_stack:
  221.         margin = self.margin_stack[-1]
  222.     else:
  223.         margin = None
  224.     self.writer.new_margin(margin, len(self.margin_stack))
  225.  
  226.     def set_spacing(self, spacing):
  227.     self.spacing = spacing
  228.     self.writer.new_spacing(spacing)
  229.  
  230.     def push_style(self, *styles):
  231.     if self.softspace:
  232.         self.hard_break = self.nospace = self.para_end = self.softspace = 0
  233.         self.writer.send_flowing_data(' ')
  234.     for style in styles:
  235.         self.style_stack.append(style)
  236.     self.writer.new_styles(tuple(self.style_stack))
  237.  
  238.     def pop_style(self, n=1):
  239.     if self.softspace:
  240.         self.hard_break = self.nospace = self.para_end = self.softspace = 0
  241.         self.writer.send_flowing_data(' ')
  242.     del self.style_stack[-n:]
  243.     self.writer.new_styles(tuple(self.style_stack))
  244.  
  245.     def assert_line_data(self, flag=1):
  246.     self.nospace = self.hard_break = not flag
  247.     self.para_end = self.have_label = 0
  248.  
  249.  
  250. class NullWriter:
  251.     """Minimal writer interface to use in testing.
  252.     """
  253.     def new_alignment(self, align): pass
  254.     def new_font(self, font): pass
  255.     def new_margin(self, margin, level): pass
  256.     def new_spacing(self, spacing): pass
  257.     def new_styles(self, styles): pass
  258.     def send_paragraph(self, blankline): pass
  259.     def send_line_break(self): pass
  260.     def send_hor_rule(self, *args, **kw): pass
  261.     def send_label_data(self, data): pass
  262.     def send_flowing_data(self, data): pass
  263.     def send_literal_data(self, data): pass
  264.  
  265.  
  266. class AbstractWriter:
  267.  
  268.     def __init__(self):
  269.     pass
  270.  
  271.     def new_alignment(self, align):
  272.     print "new_alignment(%s)" % `align`
  273.  
  274.     def new_font(self, font):
  275.     print "new_font(%s)" % `font`
  276.  
  277.     def new_margin(self, margin, level):
  278.     print "new_margin(%s, %d)" % (`margin`, level)
  279.  
  280.     def new_spacing(self, spacing):
  281.     print "new_spacing(%s)" % `spacing`
  282.  
  283.     def new_styles(self, styles):
  284.     print "new_styles(%s)" % `styles`
  285.  
  286.     def send_paragraph(self, blankline):
  287.     print "send_paragraph(%s)" % `blankline`
  288.  
  289.     def send_line_break(self):
  290.     print "send_line_break()"
  291.  
  292.     def send_hor_rule(self, *args, **kw):
  293.     print "send_hor_rule()"
  294.  
  295.     def send_label_data(self, data):
  296.     print "send_label_data(%s)" % `data`
  297.  
  298.     def send_flowing_data(self, data):
  299.     print "send_flowing_data(%s)" % `data`
  300.  
  301.     def send_literal_data(self, data):
  302.     print "send_literal_data(%s)" % `data`
  303.  
  304.  
  305. class DumbWriter(AbstractWriter):
  306.  
  307.     def __init__(self, file=None, maxcol=72):
  308.     self.file = file or sys.stdout
  309.     self.maxcol = maxcol
  310.     AbstractWriter.__init__(self)
  311.     self.reset()
  312.  
  313.     def reset(self):
  314.     self.col = 0
  315.     self.atbreak = 0
  316.  
  317.     def send_paragraph(self, blankline):
  318.     self.file.write('\n' + '\n'*blankline)
  319.     self.col = 0
  320.     self.atbreak = 0
  321.  
  322.     def send_line_break(self):
  323.     self.file.write('\n')
  324.     self.col = 0
  325.     self.atbreak = 0
  326.  
  327.     def send_hor_rule(self, *args, **kw):
  328.     self.file.write('\n')
  329.     self.file.write('-'*self.maxcol)
  330.     self.file.write('\n')
  331.     self.col = 0
  332.     self.atbreak = 0
  333.  
  334.     def send_literal_data(self, data):
  335.     self.file.write(data)
  336.     i = string.rfind(data, '\n')
  337.     if i >= 0:
  338.         self.col = 0
  339.         data = data[i+1:]
  340.     data = string.expandtabs(data)
  341.     self.col = self.col + len(data)
  342.     self.atbreak = 0
  343.  
  344.     def send_flowing_data(self, data):
  345.     if not data: return
  346.     atbreak = self.atbreak or data[0] in string.whitespace
  347.     col = self.col
  348.     maxcol = self.maxcol
  349.     write = self.file.write
  350.     for word in string.split(data):
  351.         if atbreak:
  352.         if col + len(word) >= maxcol:
  353.             write('\n')
  354.             col = 0
  355.         else:
  356.             write(' ')
  357.             col = col + 1
  358.         write(word)
  359.         col = col + len(word)
  360.         atbreak = 1
  361.     self.col = col
  362.     self.atbreak = data[-1] in string.whitespace
  363.  
  364.  
  365. def test(file = None):
  366.     w = DumbWriter()
  367.     f = AbstractFormatter(w)
  368.     if file:
  369.     fp = open(file)
  370.     elif sys.argv[1:]:
  371.     fp = open(sys.argv[1])
  372.     else:
  373.     fp = sys.stdin
  374.     while 1:
  375.     line = fp.readline()
  376.     if not line:
  377.         break
  378.     if line == '\n':
  379.         f.end_paragraph(1)
  380.     else:
  381.         f.add_flowing_data(line)
  382.     f.end_paragraph(0)
  383.  
  384.  
  385. if __name__ == '__main__':
  386.     test()
  387.